#pragma once

#include <memory>

namespace helper {
namespace resource {

//default :  T is concept of copyable
//has the ownership of T
template<typename T>
class AutoManaged {
public:
	typedef T type;
	explicit AutoManaged(type t = type())
	:_t(t)
	{
		_t.open();
	}
	~AutoManaged(){
		_t.close();
	}

  inline operator bool () const {
    return true; 
  }

	inline T & ref() {
		return _t;
	}
	inline const T & ref() const {
		return _t;
	}
  
  
  //cannot be copy
  AutoManaged(const AutoManaged & other) = delete;
  AutoManaged & operator= (const AutoManaged & other) = delete;

  //ownership is moved
  AutoManaged(AutoManaged && other)
  {
    _t = std::move(other._t);
  }
  AutoManaged & operator= (AutoManaged && other){
    _t = std::move(other._t);
    return *this;
  }

private:
	type _t;
};

//T is concept of moveable
//has ownership of T
template<typename T>
class AutoManaged<T &&> {
public:
	typedef T && type;
	explicit AutoManaged(type t)
	:_t(std::move(t))
	{
		_t.open();
	}
	~AutoManaged(){
		_t.close();
	}

  inline operator bool () const {
    return true; 
  }

	inline T & ref() {
		return _t;
	}
	inline const T & ref() const {
		return _t;
	}


  //non-copyable
  AutoManaged(const AutoManaged & other) = delete;
  AutoManaged & operator= (const AutoManaged & other) = delete;

  //ownership is moved
  AutoManaged(AutoManaged && other)
  {
    _t = std::move(other._t);
  }
  AutoManaged & operator= (AutoManaged && other){
    _t = std::move(other._t);
    return *this;
  }
private:
	T _t;
};


//T is free
//has ownership of T
template<typename T>
class AutoManaged<std::unique_ptr<T>> {
public:
	typedef std::unique_ptr<T> type;
	explicit AutoManaged()
	{
		//std::cout << "unique_ptr::ctor()"  << std::endl;
	}
	explicit AutoManaged(type & t)
	:_t(std::move(t))
	{
		//std::cout << "unique_ptr::ctor(T&)"  << std::endl;
		if(_t)
			_t->open();
	}
	AutoManaged(type && t)
	:_t(std::move(t))
	{
		//std::cout << "unique_ptr::ctor(T&&)"  << std::endl;
		if(_t)
			_t->open();
	}

	~AutoManaged(){
		if(_t)
			_t->close();
	}

  inline operator bool () const {
    return bool(_t);
  }

	inline T & ref() {
		return *_t;
	}
	inline const T & ref() const {
		return *_t;
	}


  //non-copyable
  AutoManaged(const AutoManaged & other) = delete;
  AutoManaged & operator= (const AutoManaged & other) = delete;

  //ownership is moved
  AutoManaged(AutoManaged && other)
  {
    _t = std::move(other._t);
  }
  AutoManaged & operator= (AutoManaged && other){
    _t = std::move(other._t);
    return *this;
  }
private:
	type _t;
};

//T is free
//has ownership of T
template<typename T>
class AutoManaged<std::shared_ptr<T>> {
public:
	typedef std::shared_ptr<T> type;
	explicit AutoManaged()
	{
		//std::cout << "shared_ptr::ctor()"  << std::endl;
	}
	explicit AutoManaged(const type & t = type())
	:_t(t)
	{
		//std::cout << "shared_ptr::ctor(const T&)"  << std::endl;
		if(_t)
			_t->open();
	}
	AutoManaged(type && t)
	:_t(std::move(t))
	{
		//std::cout << "shared_ptr::ctor(T&&)"  << std::endl;
		if(_t)
			_t->open();
	}

	~AutoManaged(){
		if(_t)
			_t->close();
	}

  inline operator bool () const {
    return bool(_t);
  }
	inline T & ref() {
		return *_t;
	}
	inline const T & ref() const {
		return *_t;
	}


  //non-copyable
  AutoManaged(const AutoManaged & other) = delete;
  AutoManaged & operator= (const AutoManaged & other) = delete;

  //ownership is moved
  AutoManaged(AutoManaged && other)
  {
    _t = std::move(other._t);
  }
  AutoManaged & operator= (AutoManaged && other){
    _t = std::move(other._t);
    return *this;
  }
private:
	type _t;
};

//T is free
//no ownership of T
template<typename T>
class AutoManaged<T *> {
public:
	typedef T * type;
	explicit AutoManaged(type t = nullptr)
	:_t(t)
	{
		//std::cout << "ptr:ctor(T*)" << std::endl;
		if(_t != nullptr)
			_t->open();
	}
	~AutoManaged(){
		//std::cout << "ptr:dtor(T*)" << std::endl;
		if(_t != nullptr)
			_t->close();
	}


  inline operator bool () const {
    return _t != nullptr;
  }
	inline T & ref() {
		return *_t;
	}
	inline const T & ref() const {
		return *_t;
	}


  //non-copyable
  AutoManaged(const AutoManaged & other)  = delete;
  AutoManaged & operator= (const AutoManaged & other) = delete;

  //no ownership
  AutoManaged(AutoManaged && other)
  {
    _t = std::move(other._t);
    other._t = nullptr;
  }
  AutoManaged & operator= (AutoManaged && other){
    _t = std::move(other._t);
    other._t = nullptr;
    return *this;
  }
private:
	type _t;
};

//T is free
//no ownership of T
template<typename T>
class AutoManaged<T&> {
public:
	typedef T& type;
	explicit AutoManaged(type t)
	:_t(t)
	{
		//std::cout << "ref::ctor(T&)"  << std::endl;
		_t.open();
	}
	~AutoManaged(){
		//std::cout << "ref::dtor(T&)"  << std::endl;
		_t.close();
	}

  inline operator bool () const {
    return true;
  }
	inline T & ref() {
		return _t;
	}
	inline const T & ref() const {
		return _t;
	}


  //non-copyable
  AutoManaged(const AutoManaged & other)  = delete;
  AutoManaged & operator= (const AutoManaged & other) = delete;

  //no ownership
  AutoManaged(AutoManaged && other)
  : _t(other._t)
  {
  }
  AutoManaged & operator= (AutoManaged && other){
    _t = other._t;
    return *this;
  }
private:
	type _t;
};

template<typename T, typename ... Args>
inline AutoManaged<std::unique_ptr<T>> make_auto_managed(Args&&... args){
	return AutoManaged<std::unique_ptr<T>>(std::move(std::make_unique<T>(std::forward<Args>(args)...)));
}

}//namespace resource
}//namespace helper
